/**
 * File:   DHTDataModel.java
 * Author: Gvennet
 */
package dht.node;

import dht.identifier.identifier.Identifier;
import dht.identifier.identifier.Identifiers;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * todo write javadoc
 */
public final class DHTDataModel
{

	private final List<DHTDataRow> data;

	private final List<DHTDataEventListener> listenerListData;

	private final ReentrantLock lock;

	private ScheduledThreadPoolExecutor scheduledExecutor;

	private long storageTime = 0;

	private int statistics = 0;


	public DHTDataModel()
	{
		data = new LinkedList<DHTDataRow>();
		listenerListData = new CopyOnWriteArrayList<DHTDataEventListener>();
		lock = new ReentrantLock();
	}

	public DHTDataModel(ScheduledThreadPoolExecutor scheduledExecutor, long storageTime)
	{
		data = new LinkedList<DHTDataRow>();
		listenerListData = new CopyOnWriteArrayList<DHTDataEventListener>();
		lock = new ReentrantLock();
		this.scheduledExecutor = scheduledExecutor;
		this.storageTime = storageTime;

	}

	public long getStorageTime()
	{
		return storageTime;
	}

	public void put(DHTDataRow dataRow)
	{

		lock.lock();
		try
		{
			if (!data.contains(dataRow))
			{
				for (DHTDataRow aData : data)
				{
					if (aData.getId().compareTo(dataRow.getId()) == 0 &&
							aData.getValue().equals(dataRow.getValue()))
					{
						if (aData.getTtl() < dataRow.getTtl())
						{
							data.remove(aData);
							fireRowRemoved(aData);
							data.add(dataRow);
							fireRowInserted(dataRow);
						}
						return;
					}
				}
				data.add(dataRow);
				fireRowInserted(dataRow);
			}
		}
		finally
		{
			lock.unlock();
		}
	}


	public List<DHTDataRow> get(String text)
	{
		lock.lock();

		try
		{

			List<DHTDataRow> filter = new ArrayList<DHTDataRow>();
			if (text != null && !text.isEmpty())
			{
				String[] parts = text.split(" ");
				for (String part : parts)
				{
					if (part.length() > 3)
					{
						Identifier partId = Identifiers.createIdentifier(part, 20);
						for (DHTDataRow aData : data)
						{

							if (partId.equals(aData.getId()))
							{
								filter.add(aData);
							}
						}
					}
				}
			}
			return filter;
		}
		finally
		{
			lock.unlock();
		}
	}

	public List<DHTDataRow> get(Identifier valueId)
	{
		lock.lock();

		try
		{

			List<DHTDataRow> filter = new ArrayList<DHTDataRow>();

			for (DHTDataRow aData : data)
			{

				if (valueId.equals(aData.getId()))
				{
					filter.add(aData);
				}
			}

			return filter;
		}
		finally
		{
			lock.unlock();
		}
	}

	public List<DHTDataRow> getData()
	{
		lock.lock();
		try
		{
			return new LinkedList<DHTDataRow>(data);
		}
		finally
		{
			lock.unlock();
		}
	}

	public void startStatisticsGathering(long period)
	{
		scheduledExecutor.scheduleWithFixedDelay(new StatisticsGathering(), 0, period, TimeUnit.SECONDS);
	}

	public int getStatistics()
	{
		return statistics;
	}

	public void removingOlds()
	{
		scheduledExecutor.scheduleWithFixedDelay(new RemoveOlds(), 0, 1, TimeUnit.SECONDS);
	}

	public void addListener(DHTDataEventListener listenerData)
	{
		listenerListData.add(listenerData);
	}

	public void removeListener(DHTDataEventListener listenerData)
	{
		listenerListData.remove(listenerData);
	}


	private void fireRowInserted(DHTDataRow dataRow)
	{
		for (DHTDataEventListener aListenerListData : listenerListData)
		{
			aListenerListData.rowAdded(dataRow);
		}
	}

	private void fireRowRemoved(DHTDataRow dataRow)
	{
		for (DHTDataEventListener aListenerListData : listenerListData)
		{
			aListenerListData.rowRemoved(dataRow);
		}
	}

	private void removeOlds()
	{
		long currentTime = System.currentTimeMillis();

		lock.lock();
		try
		{
			Iterator<DHTDataRow> iterator = data.iterator();
			while (iterator.hasNext())
			{
				DHTDataRow dataRow = iterator.next();
				if (dataRow.getTtl() <= currentTime)
				{
					iterator.remove();
					fireRowRemoved(dataRow);
				}
			}
		}
		finally
		{
			lock.unlock();
		}
	}

	private final class StatisticsGathering implements Runnable
	{
		@Override
		public void run()
		{
			statistics = data.size();
		}
	}

	private final class RemoveOlds implements Runnable
	{
		@Override
		public void run()
		{
			removeOlds();
		}
	}
}
